package org.xyl.engine;

import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.DocumentParser;
import dev.langchain4j.data.document.DocumentSplitter;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.document.parser.TextDocumentParser;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.pdfbox.Loader;
import dev.langchain4j.data.document.splitter.DocumentSplitters;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.store.embedding.EmbeddingMatch;
import dev.langchain4j.store.embedding.EmbeddingSearchRequest;
import dev.langchain4j.store.embedding.EmbeddingSearchResult;
import dev.langchain4j.store.embedding.EmbeddingStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.xyl.config.VectorStoreConfig;
import org.xyl.dto.RAGResponse;
import org.xyl.service.VectorLibraryServiceInterface;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.CompletableFuture;

/**
 * 企业级RAG服务实现，集成DashScope和Spring AI
 * 支持异步处理、向量存储、文档处理和智能问答
 */
@Service("enterpriseRagService")
public class EnterpriseRAGServiceImpl implements EnterpriseRAGService {

    private static final Logger logger = LoggerFactory.getLogger(EnterpriseRAGServiceImpl.class);

    @Autowired
    private EmbeddingModel embeddingModel;

    @Autowired
    private EmbeddingStore<TextSegment> embeddingStore;

    @Autowired
    private VectorStoreConfig vectorStoreConfig;

    @Autowired
    private VectorLibraryServiceInterface vectorLibraryService;

//    @Value("${dashscope.api-key:}")
    private String dashscopeApiKey = "sk-d9f42afda5924894939fbca6f80b9876";

    @Value("${rag.system-prompt:你是一个企业级问答助手，基于提供的上下文回答用户问题。请确保回答准确、简洁且仅基于上下文信息。}")
    private String systemPrompt;

    @Value("${rag.document.chunk-size:500}")
    private int chunkSize;

    @Value("${rag.document.chunk-overlap:50}")
    private int chunkOverlap;

    @Value("${rag.retrieval.top-k:3}")
    private int topK;

    /**
     * 异步处理文档，包括加载、分割、向量化和存储
     * @param documentPath 文档路径
     */
    @Override
    @Async
    public CompletableFuture<String> processDocumentAsync(String documentPath) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                processDocument(documentPath);
                return "文档处理成功: " + documentPath;
            } catch (Exception e) {
                logger.error("文档处理失败: {}", documentPath, e);
                throw new RuntimeException("文档处理失败", e);
            }
        });
    }

    /**
     * 处理文档，包括加载、分割、向量化和存储
     * @param documentPath 文档路径
     */
    @Override
    public void processDocument(String documentPath) {
        try {
            String libraryName = vectorStoreConfig.getChromaCollection();
            logger.info("开始处理文档: {}", documentPath);
            
            Path path = Paths.get(documentPath);
            String fileName = path.getFileName().toString().toLowerCase();
            String fileExtension = getFileExtension(fileName);
            logger.info("文件类型: {}", fileExtension);
            
            // 针对不同文件类型使用专门的解析器
            // 优先处理PDF文件
            if (fileExtension.equals("pdf")) {
                try {
                    logger.info("使用Apache PDFBox处理PDF文件");
                    
                    // 使用Apache PDFBox直接解析PDF
                    // 使用Apache PDFBox直接解析PDF
                    try (PDDocument pdDocument = Loader.loadPDF(path.toFile())) {
                        PDFTextStripper stripper = new PDFTextStripper();
                        String text = stripper.getText(pdDocument);
                        
                        if (isValidText(text)) {
                            logger.info("PDF文档解析成功，包含 {} 个字符，共 {} 页", 
                                       text.length(), pdDocument.getNumberOfPages());
                            
                            // 创建文档对象
                            Document document = Document.from(text);
                            
                            processDocumentSegments(document, documentPath, false);
                            return;
                        } else {
                            logger.warn("PDF解析结果不包含有效文本");
                        }
                    }
                } catch (Exception e) {
                    logger.warn("PDFBox处理遇到问题: {}", e.getMessage());
                }
            }
            
            // 对于非PDF文件或PDF解析失败的情况，尝试使用通用的文本解析器
            try {
                // 尝试使用文本解析器
                DocumentParser parser = new TextDocumentParser();
                Document document = FileSystemDocumentLoader.loadDocument(path, parser);
                
                // 检查解析结果是否合理（非空且不都是乱码）
                String text = document.text();
                if (isValidText(text)) {
                    logger.info("文档解析成功，使用文本模式处理");
                    processDocumentSegments(document, documentPath, false);
                    return;
                }
            } catch (Exception e) {
                logger.warn("文本模式解析遇到问题: {}", e.getMessage());
            }
            
            // 如果标准解析失败，使用二进制安全的方式处理文件
            logger.info("切换到二进制安全方式处理文件");
            
            // 读取文件内容作为二进制数据的文本表示
            byte[] fileContent = Files.readAllBytes(path);
            // 将二进制数据转换为十六进制字符串（避免乱码问题）
            StringBuilder hexString = new StringBuilder();
            for (byte b : fileContent) {
                hexString.append(String.format("%02x", b));
                // 每1000个字节添加一个换行符，便于分割
                if (hexString.length() % 2000 == 0) {
                    hexString.append("\n");
                }
            }
            
            // 创建文档对象 - 不使用Metadata，直接使用文本内容
            Document binaryDocument = Document.from(hexString.toString());
            processDocumentSegments(binaryDocument, documentPath, true);
            
        } catch (Exception e) {
            logger.error("文档处理失败: {}", documentPath, e);
            throw new RuntimeException("文档处理失败", e);
        }
    }
    
    /**
     * 处理文档片段的通用方法
     * @param document 文档对象
     * @param documentPath 文档路径
     * @param isBinaryMode 是否为二进制模式
     */
    private void processDocumentSegments(Document document, String documentPath, boolean isBinaryMode) {
        try {
            // 设置合适的分块大小
            String libraryName = vectorStoreConfig.getChromaCollection();
            int effectiveChunkSize = isBinaryMode ? chunkSize * 2 : chunkSize;
            int effectiveChunkOverlap = isBinaryMode ? chunkOverlap * 2 : chunkOverlap;
            
            // 分割文档为块
            DocumentSplitter splitter = DocumentSplitters.recursive(effectiveChunkSize, effectiveChunkOverlap);
            List<TextSegment> segments = splitter.split(document);
            
            logger.info("文档分割完成，共 {} 个片段", segments.size());
            
            // 生成嵌入向量
            List<Embedding> embeddings = embeddingModel.embedAll(segments).content();
            
            // 存储嵌入向量和文本片段
            embeddingStore.addAll(embeddings, segments);
            
            // 更新向量库元数据
//            String libraryName = "default";
            vectorLibraryService.createVectorLibrary(libraryName);
            
            for (TextSegment segment : segments) {
                // 只传递文本内容，不传递复杂的metadata
                vectorLibraryService.addDocumentToLibrary(libraryName, segment.text(), null);
            }
            
            logger.info("文档处理完成: {}", documentPath);
        } catch (Exception e) {
            logger.error("处理文档片段失败", e);
            throw new RuntimeException("处理文档片段失败", e);
        }
    }

    /**
     * RAG查询，包括查询向量化、相似文档检索和大模型回答生成
     * @param question 用户问题
     * @return 回答结果
     */
    @Override
    public RAGResponse ragQuery(String question) {
        return ragQuery(question, null);
    }

    /**
     * 企业级RAG问答，集成向量检索和LLM生成
     * @param question 用户问题
     * @param vectorLibraryName 向量库名称（可选）
     * @return RAG增强的回答
     */
    @Override
    public RAGResponse ragQuery(String question, String vectorLibraryName) {
        long startTime = System.currentTimeMillis();
        RAGResponse response = new RAGResponse();
        response.setQuestion(question);
        
        try {
            logger.info("处理RAG查询: {}", question);
            
            // 1. 生成查询嵌入向量
            Embedding queryEmbedding = embeddingModel.embed(question).content();
            
            // 2. 检索相关文档片段
            List<EmbeddingMatch<TextSegment>> relevantSegments;
            if (vectorLibraryName != null && !vectorLibraryName.isEmpty()) {
                relevantSegments = vectorLibraryService.searchInVectorLibrary(vectorLibraryName, queryEmbedding, topK);
            } else {
                EmbeddingSearchRequest request = new EmbeddingSearchRequest(queryEmbedding, topK, 0.30d,null);
                EmbeddingSearchResult<TextSegment> search = embeddingStore.search(request);
                relevantSegments = search.matches();
            }
            
            // 3. 构建上下文和相关上下文列表
            StringBuilder contextBuilder = new StringBuilder();
            List<String> contexts = new ArrayList<>();
            List<Double> scores = new ArrayList<>();
            
            for (int i = 0; i < relevantSegments.size(); i++) {
                EmbeddingMatch<TextSegment> match = relevantSegments.get(i);
                String segmentText = match.embedded().text();
                contexts.add(segmentText);
                scores.add(match.score());
                contextBuilder.append("[文档片段").append(i + 1).append("]")
                        .append(segmentText)
                        .append("\n\n");
            }
            
            response.setRelevantContexts(contexts);
            response.setSources(scores);
            
            String context = contextBuilder.toString();
            
            // 4. 构建提示
            String prompt = buildPrompt(context, question);
            
            // 5. 使用DashScope生成回答
            String answer = generateWithDashScope(prompt);
            response.setAnswer(answer);
            
            logger.info("RAG查询处理完成");
        } catch (Exception e) {
            logger.error("RAG查询失败", e);
            response.setAnswer("查询处理失败: " + e.getMessage());
            response.setFallbackUsed(true);
        } finally {
            response.setProcessingTime(System.currentTimeMillis() - startTime);
        }
        
        return response;
    }

    /**
     * 异步RAG查询
     * @param question 用户问题
     * @param vectorLibraryName 向量库名称（可选）
     * @return 异步查询结果
     */
    @Async
    public CompletableFuture<RAGResponse> ragQueryAsync(String question, String vectorLibraryName) {
        return CompletableFuture.supplyAsync(() -> ragQuery(question, vectorLibraryName));
    }

    /**
     * 构建RAG提示
     * @param context 上下文信息
     * @param question 用户问题
     * @return 完整提示
     */
    @Override
    public String buildPrompt(String context, String question) {
        return String.format("%s\n\n[上下文信息]\n%s\n\n[用户问题]\n%s\n\n[回答要求]\n" +
                        "1. 仅基于提供的上下文信息回答问题\n" +
                        "2. 如果无法从上下文获取答案，请说明无法回答\n" +
                        "3. 回答应当准确、简洁、专业\n" +
                        "4. 必要时可以引用具体文档片段",
                systemPrompt, context, question);
    }

    /**
     * 使用DashScope生成回答
     * @param prompt 提示文本
     * @return 生成的回答
     * @throws ApiException API异常
     * @throws NoApiKeyException 缺少API密钥异常
     */
    @Override
    public String generateWithDashScope(String prompt) throws ApiException, NoApiKeyException {
        // 设置API密钥
        if (!dashscopeApiKey.isEmpty()) {
            System.setProperty("dashscope.api.key", dashscopeApiKey);
        }
        
        // 配置生成参数
        GenerationParam param = GenerationParam.builder()
                .model("qwen-plus") // 使用通义千问Plus模型
                .prompt(prompt)
                .topP(0.8)
                .maxTokens(2048)
                .build();
        
        // 创建生成器
        Generation gen = new Generation();
        
        // 生成回答
        GenerationResult result = null;
        try {
            result = gen.call(param);
        } catch (InputRequiredException e) {
            throw new RuntimeException(e);
        }

        // 返回结果
        return result.getOutput().getText();
    }

    /**
     * 批量处理多个文档
     * @param documentPaths 文档路径列表
     */
    @Override
    public void batchProcessDocuments(List<String> documentPaths) {
        documentPaths.parallelStream().forEach(this::processDocument);
    }

    /**
     * 清空向量存储
     */
    @Override
    public void clearVectorStore() {
        // 这里应该有清空向量存储的实现
        // 实际实现取决于使用的向量存储类型
        logger.info("清空向量存储");
    }

    /**
     * 获取向量存储状态
     * @return 存储状态信息
     */
    @Override
    public Map<String, Object> getVectorStoreStats() {
        // 这里应该有获取向量存储状态的实现
        return Map.of(
                "storeType", embeddingStore.getClass().getSimpleName(),
                "status", "operational"
        );
    }
    
    /**
     * 生成简单回答
     * @param question 用户问题
     * @param context 上下文信息
     * @return 生成的回答
     */
    @Override
    public String generateSimpleAnswer(String question, String context) {
        try {
            String prompt = buildPrompt(context, question);
            return generateWithDashScope(prompt);
        } catch (Exception e) {
            logger.error("生成简单回答失败: {}", e.getMessage(), e);
            return "抱歉，我无法生成回答，请稍后重试。";
        }
    }
    
    /**
     * 获取文件扩展名
     * @param fileName 文件名
     * @return 文件扩展名（小写）
     */
    private String getFileExtension(String fileName) {
        int lastDotIndex = fileName.lastIndexOf('.');
        if (lastDotIndex > 0 && lastDotIndex < fileName.length() - 1) {
            return fileName.substring(lastDotIndex + 1).toLowerCase();
        }
        return "unknown";
    }
    
    /**
     * 检查文本是否有效（非空且不都是乱码）
     * @param text 待检查的文本
     * @return 是否为有效文本
     */
    private boolean isValidText(String text) {
        if (text == null || text.trim().isEmpty()) {
            return false;
        }
        
        // 计算可打印字符的比例
        int printableCount = 0;
        int whitespaceCount = 0;
        
        for (char c : text.toCharArray()) {
            if (Character.isWhitespace(c)) {
                whitespaceCount++;
            } else if (Character.isISOControl(c)) {
                // 控制字符，可能是乱码的一部分
            } else {
                printableCount++;
            }
        }
        
        int totalNonWhitespace = text.length() - whitespaceCount;
        
        // 如果可打印字符比例过低，认为是乱码
        return totalNonWhitespace == 0 || (printableCount * 100.0 / totalNonWhitespace) > 50;
    }
}